﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace FileListMaker
{
    public class DefaultFileSeekProcesser : IFileSeekProcesser
    {
        public string[] SeekFile(string dirPath)
        {
            //例外発生時(必要パーミッションがないなど)の場合は空配列を返す。

            try
            {
                return Directory.GetFiles(dirPath, "*", SearchOption.TopDirectoryOnly);
            }
            catch
            {
                return new string[0];
            }
        }

        public string[] SeekDir(string dirPath)
        {
            //例外発生時(必要パーミッションがないなど)の場合は空配列を返す。

            try
            {
                return Directory.GetDirectories(dirPath, "*", SearchOption.TopDirectoryOnly);
            }
            catch
            {
                return new string[0];
            }
        }
    }

    public class ListServiceResult : IListServiceResult
    {
        private List<string> result = new List<string>();

        public ListServiceResult()
        {
        }

        public void Add(string fileFullPath)
        {
            result.Add(fileFullPath);
        }

        public void AddRange(IEnumerable<string> fileFullPaths)
        {
            result.AddRange(fileFullPaths);
        }

        public string[] GetResult()
        {
            return result.ToArray();
        }
    }

    public class DefaultListService : IListService
    {
        public IListCondition Condition { get; set; }

        private IFileSeekProcesser fileSeeker = null;
        public IFileSeekProcesser FileSeeker
        {
            get
            {
                if (fileSeeker == null)
                    fileSeeker = new DefaultFileSeekProcesser();
                return fileSeeker;
            }

            set
            {
                fileSeeker = value;
            }
        }

        private IListServiceResult serviceResult = null;
        public IListServiceResult ListServiceResult
        {
            get
            {
                if (serviceResult == null)
                    serviceResult = new ListServiceResult();
                return serviceResult;
            }

            set
            {
                serviceResult = value;
            }
        }


        private bool? stopped = null;

        public void Stop()
        {
            if (!stopped.HasValue)
                return;
            stopped = true;
        }

        public string[] GetResult(string rootDirPath)
        {
            stopped = false;
            _getResult(rootDirPath);
            stopped = null;
            return ListServiceResult.GetResult();
        }

        private void _getResult(string dirPath)
        {
            if (stopped.HasValue && stopped.Value)
                return;

            var files = FileSeeker.SeekFile(dirPath);
            foreach(var file in files)
            {
                if(Condition == null || Condition.IsMatch(file))
                    ListServiceResult.Add(file);
            }

            var dirs = FileSeeker.SeekDir(dirPath);
            foreach (var dir in dirs)
            {
                _getResult(dir);   
            }
        }
    }

    public class AsyncDefaultListService : IAsyncListService
    {
        private class NotificationListServiceResult : IListServiceResult
        {
            private List<string> result = new List<string>();
            private AsyncDefaultListService parent;

            public NotificationListServiceResult(AsyncDefaultListService parent)
            {
                this.parent = parent;
            }

            public void Add(string fileFullPath)
            {
                result.Add(fileFullPath);
                parent.OnFindFile(fileFullPath);
            }

            public void AddRange(IEnumerable<string> fileFullPaths)
            {
                foreach (var p in fileFullPaths)
                    Add(p);
            }

            public string[] GetResult()
            {
                return result.ToArray();
            }
        }

        public event EventHandler<FindFileEventArgs> FindFile;
        public event EventHandler<FinishFindEventArgs> FinishFind;

        private IListService innerService = null;

        internal IListService InnerService
        {
            get
            {
                if (innerService == null)
                {
                    innerService = new DefaultListService() { ListServiceResult = new NotificationListServiceResult(this) };
                }
                return innerService;
            }
            set
            {
                innerService = value;
            }
        }

        public AsyncDefaultListService()
        {
        }

        public AsyncDefaultListService(IListCondition cond)
        {
            InnerService.Condition = cond;
        }

        public AsyncDefaultListService(IListService listService, IFileSeekProcesser seeker)
        {
            this.innerService = listService;
            innerService.FileSeeker = seeker;
            innerService.ListServiceResult = new NotificationListServiceResult(this);
        }

        protected void OnFindFile(string filePath)
        {
            if (FindFile != null)
                FindFile(this, new FindFileEventArgs(filePath));
        }

        protected void OnFinishFind(string[] result)
        {
            if (FinishFind != null)
                FinishFind(this, new FinishFindEventArgs(result));
        }

        public void Stop()
        {
            InnerService.Stop();
            workThread.Abort();
            workThread.Join();
        }

        private System.Threading.Thread workThread;

        public void GetResultAsync(string rootDirPath)
        {
            workThread = new System.Threading.Thread(delegate()
            {
                try
                {
                    string[] result = InnerService.GetResult(rootDirPath);
                    OnFinishFind(result);
                }
                catch (System.Threading.ThreadAbortException)
                {
                }
            });
            workThread.Start();
        }
    }
}
